{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 卦"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "dotnet_interactive": {
     "language": "csharp"
    },
    "polyglot_notebook": {
     "kernelName": "csharp"
    },
    "vscode": {
     "languageId": "polyglot-notebook"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div><div></div><div></div><div><strong>Installed Packages</strong><ul><li><span>YiJingFramework.PrimitiveTypes, 4.0.1</span></li></ul></div></div>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "#r \"nuget:YiJingFramework.PrimitiveTypes\"\n",
    "using YiJingFramework.PrimitiveTypes;"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "卦是由若干具有阴阳属性的爻组成的。通常来说，常用的卦一般只具有三根爻（八卦）或六根爻（六十四卦），但是这里的 `Gua` 支持任意的爻数。\n",
    "\n",
    "> [YiJingFramework.PrimitiveTypes.GuaWithFixedCount](https://yjfwk.yueyinqiu.top/PrimitiveTypes.GuaWithFixedCount/) 提供了只支持固定爻数的卦，从而能够及时在编译期发现错误。\n",
    "\n",
    "## 创建\n",
    "\n",
    "`Gua` 其实就是一个不可变的、有序的爻的集合，可以通过以下方式来创建它："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "dotnet_interactive": {
     "language": "csharp"
    },
    "polyglot_notebook": {
     "kernelName": "csharp"
    },
    "vscode": {
     "languageId": "polyglot-notebook"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "110\n",
      "111111\n"
     ]
    }
   ],
   "source": [
    "var dui = new Gua(Yinyang.Yang, Yinyang.Yang, Yinyang.Yin);\n",
    "Console.WriteLine(dui);\n",
    "\n",
    "var qian = new Gua(Enumerable.Repeat(Yinyang.Yang, 6));\n",
    "Console.WriteLine(qian);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "虽然其顺序本身是可以颠倒的，但是我们约定取易气上行、先来居下、由下至上之意，即序号小的在下、序号大的在上（所谓上下即常用的卦画的上下），如上例“阳阳阴”表示兑卦而非巽卦。包括 [YiJingFramework.EntityRelations](https://yjfwk.yueyinqiu.top/EntityRelations/) 和 [YiJingFramework.Annotating.Zhouyi](https://yjfwk.yueyinqiu.top/Annotating.Zhouyi/) 在内的各种包，都是按照由下至上的理解方式来提供功能的。\n",
    "\n",
    "`Gua` 实现了 `IReadOnlyList` ，因此可以当作列表使用。无论是通过索引器访问，还是通过 `foreach` 遍历，都是由下至上的顺序："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "dotnet_interactive": {
     "language": "csharp"
    },
    "polyglot_notebook": {
     "kernelName": "csharp"
    },
    "vscode": {
     "languageId": "polyglot-notebook"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "110\n",
      "阳阳阴\n",
      "Yang,Yang,Yin\n"
     ]
    }
   ],
   "source": [
    "var dui = new Gua(Yinyang.Yang, Yinyang.Yang, Yinyang.Yin);\n",
    "\n",
    "Console.WriteLine(dui);\n",
    "Console.WriteLine($\"{dui[0]:C}{dui[1]:C}{dui[2]:C}\");\n",
    "Console.WriteLine($\"{string.Join(',', dui)}\");"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Gua 和 string 的相互转换\n",
    "\n",
    "先前的示例已经显示了 `Gua.ToString` 的效果。这里考虑到 `Gua` 支持任意的爻数，让它返回 `\"乾\"` 、 `\"兑\"` 、 `\"离\"` 、 `\"震\"` 等结果并不合适。因此不同于[五行](./五行.ipynb)、[干支](./天干地支.ipynb)、[阴阳](./阴阳.ipynb)等类型，它返回的是一组数字，其中 `0` 表示阴爻、 `1` 表示阳爻。如果需要得到卦名，可以尝试使用 [YiJingFramework.Annotating.Zhouyi](https://yjfwk.yueyinqiu.top/Annotating.Zhouyi/) 。\n",
    "\n",
    "> 如果仅仅只是为了得到卦名， [YiJingFramework.Annotating.Zhouyi](https://yjfwk.yueyinqiu.top/Annotating.Zhouyi/) 可能有些笨重了，或许自己实现一个转换还是比较方便的。\n",
    "\n",
    "同样的，`Gua` 也提供了 `Parse` 和 `TryParse` 方法，支持从字符串转换到 `Gua` 。\n",
    "\n",
    "## Gua 和 byte[] 的相互转换\n",
    "\n",
    "除 `string` 外， `Gua` 和 `byte[]` 之间也可以相互转换："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "dotnet_interactive": {
     "language": "csharp"
    },
    "polyglot_notebook": {
     "kernelName": "csharp"
    },
    "vscode": {
     "languageId": "polyglot-notebook"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "111101101000101011101110111110\n",
      "6F51775F\n",
      "True\n"
     ]
    }
   ],
   "source": [
    "static IEnumerable<Yinyang> GetRandomLines()\n",
    "{\n",
    "    Random random = new Random(0);\n",
    "    for (; ; )\n",
    "        yield return (Yinyang)random.Next(0, 2);\n",
    "}\n",
    "\n",
    "var gua = new Gua(GetRandomLines().Take(30));\n",
    "Console.WriteLine(gua);\n",
    "\n",
    "var byteArray = gua.ToBytes();\n",
    "Console.WriteLine(Convert.ToHexString(byteArray));\n",
    "\n",
    "var gua2 = Gua.FromBytes(byteArray);\n",
    "Console.WriteLine(gua == gua2);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这种转换一般只会在非常特殊的场合下使用。而且即使需要使用，一般也不需要关注得到的 `byte[]` 具体是什么值，只需要知道它可以正确还原就足够了。\n",
    "\n",
    "在转换时，会使用 `1` 表示阳、 `0` 表示阴，越低位所表示的爻也越靠下。由于爻的个数是不确定的，在最高位会额外写一个 `1` 以表示解析的起点（也可以叫终点）。举例而言，兑卦“阳阳阴”，转为数字得 `[1, 1, 0]`（左侧是低位，右侧是高位）；然后最高位添一，得 `[1, 1, 0, 1]` ；填充为八的整数倍，得 `[1, 1, 0, 1, 0, 0, 0, 0]` ，这就是最后的结果了。但常规的数字写法是左高右低的，即得到的是 `[0b0000_1011]` 。又如“阳阳阳阴阳阳阳阳阳”：\n",
    "\n",
    "```text\n",
    "   阳阳阳阴 阳阳阳阳 阳\n",
    "-> 1,1,1,0, 1,1,1,1, 1\n",
    "-> 1,1,1,0, 1,1,1,1, 1,1 \n",
    "-> (1110 1111) (1100 0000)\n",
    "-> 0b0000_0011 at bytes[1]\n",
    "   0b1111_0111 at bytes[0]\n",
    "-> [0b1111_0111, 0b0000_0011]\n",
    "      8765 4321           |9\n",
    "```\n",
    "\n",
    "其中特别要注意的是，最高位的 `1` 表示结束，而不是一根爻，因此比如在取错卦时，不能把它一同取反。因此，这样的转换其实并没有太大的作用，只有非常特殊的情况下可能用到。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "file_extension": ".cs",
   "mimetype": "text/x-csharp",
   "name": "python",
   "pygments_lexer": "csharp",
   "version": "3.12.2"
  },
  "polyglot_notebook": {
   "kernelInfo": {
    "defaultKernelName": "csharp",
    "items": [
     {
      "aliases": [],
      "name": "csharp"
     }
    ]
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
